/*!
** @file uploader.js
** @brief Functions and objects which are uploading content
** @see keepmybookmarks.js : main file that calls this one
** @author Adrien MOGENET
*/


/* HTTP error codes */
const HTTP_NOERR		= 200;
const HTTP_CREATED		= 201;
const HTTP_NOCONTENT		= 204;
const HTTP_LOGINFAILURE		= 401;
const HTTP_BADMETHOD		= 405;

/* Network error codes */
const ERR_NOERR			= 0;
const ERR_UNEXPECTED		= 1;
const ERR_USERCANCEL		= 2;
const ERR_REFUSED		= 13;
const ERR_NETTIMEOUT		= 14;
const ERR_NETOFFLINE		= 16;

/* FTP error codes */
const ERR_LOGINFAILURE		= 21;
const ERR_FTPCWD		= 22;
const ERR_FTPASV		= 23;
const ERR_FTPPWD		= 24;
const ERR_FTPLIST		= 25;
const ERR_UNKNOWN		= 30;


/*!
** @brief Callback function, at the end of upload.
** Here, handles the returned code and informs the user if there
** was an error.
** @param string: status
** @param int	: error
** @return bool	: true if no error
** @author Adrien MOGENET
** @todo Adapt the behaviour
** @todo Errors are not working...
*/
function uploadCallback (status, error)
{
    var code	= error % 0x804b0000;
    var message	= "";

    if (error == ERR_NOERR)
    {
	if (DEBUG)
	    alert ('Upload OK');
	return true;
    }

    switch (code)
    {
      case ERR_UNEXPECTED:
        message = "unexpected";
        break;
      case ERR_USERCANCEL:
        message = "usercancel";
        break;
      case ERR_REFUSED:
        message = "refused";
        break;
      case ERR_NETTIMEOUT:
        message = "netTimeout";
        break;
      case ERR_NETOFFLINE:
        message = "netOffline";
        break;
      case ERR_LOGINFAILURE:
        message = "loginfailure"
        break;
      case ERR_FTPCWD:
        message = "ftpcwd"
        break;
      case ERR_FTPASV:
        message = "ftppasv"
        break;
      case ERR_FTPPWD:
        message = "ftppwd"
        break;
      case ERR_FTPLIST:
        message = "ftplist"
        break;
      case ERR_UNKNOWN:
        message = "unknown"
        break;
      case HTTP_CREATED:
        message = "created"
        break;
      case HTTP_LOGINFAILURE:
        message = "loginfailure"
        break;
      case HTTP_BADMETHOD:
        message = "methodNotAllowed"
        break;
      default:
        message = "unexpected";
        break;
    }

    if (DEBUG)
	alert ('Upload error' + message);

    return false;
}

/*!
** @brief Uploader object
** @author Adrien MOGENET
*/
var uploader =
{
    _channel	: null,
    _callback	: null,
    _data	: "",
    _scheme	: "",
    _errorData	: "",
    _status	: 0,

/*!
** @brief Performs the upload
** @param string: scheme (ftp, http...)
** @param string: username
** @param string: password
** @param string: host
** @param string: path
** @param int	: port
** @param string: content to upload
** @return bool	: true if no error
** @author Adrien MOGENET
** @todo Add a @c port option
** @todo Set 'charset' as a preference ?
*/
    upload: function (scheme, user, password, host, path, port, content)
    {
	const ioService	= Components.classes["@mozilla.org/network/io-service;1"]
			.getService (Components.interfaces.nsIIOService);
	const stringStr	= Components.classes["@mozilla.org/io/string-input-stream;1"]
			.createInstance (Components.interfaces.nsIStringInputStream);
	var uri		= scheme + "://" + user + ":" + password + "@" + host + path;
	var charset	= "UTF-8";
	var mimetype	= "text/html; charset=UTF-8";
	var iUri	= ioService.newURI (uri, charset, null);

	this._scheme	= scheme;
	this._callback	= uploadCallback;
	this._channel	= ioService.newChannelFromURI (iUri)
			.QueryInterface (Components.interfaces.nsIUploadChannel);

	try
	{
	    stringStr.setData (content, -1);
	    this._channel.setUploadStream (stringStr, mimetype, -1);
	    this._channel.asyncOpen (this, null);
	    this._data = content;
	    return true;
	}
	catch (error)
	{
	    if (DEBUG)
		alert ("Error:\n" + error);
	}

	return false;
    },

/*!
** @brief Action if the upload is cancelled
** @author Adrien MOGENET
*/
    cancel: function ()
    {
	if(this._channel)
	    this._channel.cancel (0x804b0002);
    },

/*!
** @brief Store the data that the server send in response.
** @param channel
** @param context
** @param input
** @param sourceOffset
** @param count
** @author Adrien MOGENET
*/
    onDataAvailable: function (channel, context, input, sourceOffset, count)
    {
	const stream	= Components.classes["@mozilla.org/scriptableinputstream;1"]
			.createInstance (Components.interfaces.nsIScriptableInputStream);
	stream.init (input);
	this._errorData += stream.read (count);
    },


/*!
** @brief Implementation of the listener method
** @see http://developer.mozilla.org/en/docs/nsIChannel
** @param channel
** @param context
** @author Adrien MOGENET
*/
    onStartRequest: function (channel, context) { },


/*!
** @brief End of the request. Call the callback function if defined.
** @see http://developer.mozilla.org/en/docs/nsIChannel
** @param channel
** @param context
** @param status
** @author Adrien MOGENET
*/
    onStopRequest: function (channel, context, status)
    {
	if (this._scheme != "ftp")
	{
	    var res = 0;
	    try
	    {
		res = channel.QueryInterface (Components.interfaces.nsIHttpChannel)
		    .responseStatus;
	    }
	    catch (error)
	    {
		if (DEBUG)
		    alert ('onStopRequest Error' + error);
	    }
	    if (res == HTTP_NOERR || res == HTTP_CREATED || res == HTTP_NOCONTENT)
		status = 0;
	    if (this._errorData)
		status = res;
	    if (this._errorData && res == HTTP_NOERR)
		alert (this._errorData);
	}
	if (this._callback)
	    this._callback ("done", status);
    }
};
